package smallfrench.moteur.biblio;

import java.math.BigDecimal;

import smallfrench.moteur.biblio.err.ErreurMath;
import smallfrench.moteur.interp.RObjet;
import smallfrench.moteur.interp.err.RException;

/**
 * Un nombre entier immuable.
 * @author antoine1023
 *
 */
public class REntier extends RObjet {

	public final long valeur;

	public REntier(long valeur) {
		super(RClasseEntier.CLASSE);
		this.valeur = valeur;
	}

	//-------------------------------------------

	/**
	 * 
	 * @return La valeur convertie du {@link REntier}.
	 */
	public int getValeurInt() {
		return (int) valeur;
	}

	//-------------------------------------------

	@Override
	public boolean equals(Object objet) {
		if (objet instanceof REntier) {
			REntier t = (REntier)objet;
			return t.valeur == valeur;
		}
		return false;
	}

	//------------------------------------------- Opérateurs binaires

	@Override
	public RObjet operateurPlusBase(RObjet o) {

		if (o instanceof REntier) {
			return new REntier(valeur + ((REntier)o).valeur);
		} else if (o instanceof RFlottant) {
			return new RFlottant(valeur + ((RFlottant)o).valeur);
		} else if (o instanceof RChaine) {
			return new RChaine(valeur + ((RChaine)o).valeur);
		} else if (o instanceof RDecimal) {
			// Ca marche parce que a + b == b + a
			return ((RDecimal) o).operateurPlusBase(this);
		}
		return null;
	}

	@Override
	public RObjet operateurMoinsBase(RObjet o) {

		if (o instanceof REntier) {
			return new REntier(valeur - ((REntier)o).valeur);
		} else if (o instanceof RFlottant) {
			return new RFlottant(valeur - ((RFlottant)o).valeur);
		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			BigDecimal r = new BigDecimal(valeur).
					subtract(bd, RDecimal.MATH_CONTEXT);
			return new RDecimal(r);
		}
		return null;
	}

	@Override
	public RObjet operateurEtoileBase(RObjet o) {

		if (o instanceof REntier) {
			return new REntier(valeur * ((REntier)o).valeur);
		} else if (o instanceof RFlottant) {
			return new RFlottant(valeur * ((RFlottant)o).valeur);
		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			BigDecimal r = new BigDecimal(valeur).
					multiply(bd, RDecimal.MATH_CONTEXT);
			return new RDecimal(r);
		}
		return null;
	}

	@Override
	public RObjet operateurSlashBase(RObjet o) throws RException {

		if (o instanceof REntier) {

			long v = ((REntier)o).valeur;
			if (v == 0L)
				throw ErreurMath.nouvelleDivisionPar0();

			return new REntier(valeur / ((REntier)o).valeur);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			if (bd.compareTo(BigDecimal.ZERO) == 0)
				throw ErreurMath.nouvelleDivisionPar0();

			BigDecimal r = new BigDecimal(valeur).
					divide(bd, RDecimal.MATH_CONTEXT);
			return new RDecimal(r);

		} else if (o instanceof RFlottant) {
			double f = ((RFlottant)o).valeur;
			if (f == 0.0)
				throw ErreurMath.nouvelleDivisionPar0();
			return new RFlottant(valeur / f);
		}
		return null;
	}

	@Override
	public RObjet operateurModuloBase(RObjet o) throws RException {

		if (o instanceof REntier) {

			long v = ((REntier)o).valeur;
			if (v == 0L)
				throw ErreurMath.nouvelleModuloPar0();

			return new REntier(valeur % v);

		} else if (o instanceof RDecimal) {

			BigDecimal bd = ((RDecimal)o).valeur;
			if (bd.compareTo(BigDecimal.ZERO) == 0)
				throw ErreurMath.nouvelleDivisionPar0();

			BigDecimal r = new BigDecimal(valeur).
					remainder(bd, RDecimal.MATH_CONTEXT);
			return new RDecimal(r);

		} else if (o instanceof RFlottant) {
			double f = ((RFlottant)o).valeur;
			if (f == 0.0)
				throw ErreurMath.nouvelleDivisionPar0();
			return new RFlottant(valeur % f);
		}
		return null;
	}

	//----------------------------------------- comparaisons

	/**
	 * Indéfini avec {@link RFlottant}, n’a pas de sens à cause de la précision
	 * limitée.
	 */
	@Override
	public RBooleen operateurEgalBase(RObjet o) {

		if (o instanceof REntier) {
			return RBooleen.nouveau(valeur == ((REntier)o).valeur);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			return RBooleen.nouveau(new BigDecimal(valeur)
			.compareTo(bd) == 0);
		}
		return null;
	}

	/**
	 * Indéfini avec {@link RFlottant}, n’a pas de sens à cause de la précision
	 * limitée.
	 */
	@Override
	public RBooleen operateurInegalBase(RObjet o) {

		if (o instanceof REntier) {
			return RBooleen.nouveau(valeur != ((REntier)o).valeur);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			return RBooleen.nouveau(new BigDecimal(valeur)
			.compareTo(bd) != 0);
		}
		return null;
	}

	@Override
	public RBooleen operateurInfBase(RObjet o) {

		if (o instanceof REntier) {
			return RBooleen.nouveau(valeur < ((REntier)o).valeur);

		} else if (o instanceof RFlottant) {
			return RBooleen.nouveau(valeur < ((RFlottant)o).valeur);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			return RBooleen.nouveau(new BigDecimal(valeur)
			.compareTo(bd) == -1);
		}
		return null;
	}

	@Override
	public RBooleen operateurSupBase(RObjet o) {

		if (o instanceof REntier) {
			return RBooleen.nouveau(valeur > ((REntier)o).valeur);

		} else if (o instanceof RFlottant) {
			return RBooleen.nouveau(valeur > ((RFlottant)o).valeur);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			return RBooleen.nouveau(new BigDecimal(valeur)
			.compareTo(bd) == 1);
		}
		return null;
	}

	/**
	 * Indéfini avec {@link RFlottant}, n’a pas de sens à cause de la précision
	 * limitée.
	 */
	@Override
	public RBooleen operateurInfegalBase(RObjet o) {

		if (o instanceof REntier) {
			return RBooleen.nouveau(valeur <= ((REntier)o).valeur);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			return RBooleen.nouveau(new BigDecimal(valeur)
			.compareTo(bd) <= 0);
		}
		return null;
	}

	/**
	 * Indéfini avec {@link RFlottant}, n’a pas de sens à cause de la précision
	 * limitée.
	 */
	@Override
	public RBooleen operateurSupegalBase(RObjet o) {

		if (o instanceof REntier) {
			return RBooleen.nouveau(valeur >= ((REntier)o).valeur);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			return RBooleen.nouveau(new BigDecimal(valeur)
			.compareTo(bd) >= 0);
		}
		return null;
	}

	//------------------------------------------- Opérateurs unaires

	@Override
	public RObjet operateurMoinsUnaire()
			throws RException {
		return new REntier(- valeur);
	}

	//------------------------------------------- Membres

	@Override
	public RObjet getMembre(String nom) {
		return RClasseEntier.CLASSE.champs.get(nom);
	}

	//------------------------------------------- Strings

	public String getNom() {
		return "Entier";
	}

	@Override
	public RChaine enChaine() {
		return new RChaine(Long.toString(valeur));
	}

}

